function testLog() {
    baseLog("/////////////////////////////////////////////////////////////////");
    baseLog("Hello chapter-7.js!");
}

function testIteratorPattern() {
    {
        let num = 1;
        let obj = {};
        baseLog(num[Symbol.iterator]);                  // undefined
        baseLog(obj[Symbol.iterator]);                  // undefined
        let str = 'abc';
        let arr = ['a', 'b', 'c'];
        let map = new Map().set('a', 1).set('b', 2).set('c', 3);
        let set = new Set().add('a').add('b').add('c');
        let els = document.querySelector('div');
        baseLog(str[Symbol.iterator]);                  // ƒ [Symbol.iterator]() { [native code] }
        baseLog(arr[Symbol.iterator]);                  // ƒ values() { [native code] }
        baseLog(map[Symbol.iterator]);                  // ƒ entries() { [native code] }
        baseLog(set[Symbol.iterator]);                  // ƒ values() { [native code] }
        // baseLog(els[Symbol.iterator]);                  // f values() {[navite code]}
        baseLog(str[Symbol.iterator]());                // StringIterator {}
        baseLog(arr[Symbol.iterator]());                // ArrayIterator {}
        baseLog(map[Symbol.iterator]());                // MapIterator {}
        baseLog(set[Symbol.iterator]());                // SetIterator {}
        // baseLog(els[Symbol.iterator]());                // StringIterator
    }
    {
        class Counter {
            constructor(limit) {
                this.limit = limit;
            }
            [Symbol.iterator]() {
                let count = 1;
                let limit = this.limit;
                return {
                    next() {
                        if (count <= limit) {
                            return {done: false, value: count++};
                        } else {
                            return {done: true, value: undefined};
                        }
                    },
                    return() {
                        baseLog('Exitingearly');
                        return {done: true};
                    }
                };
            }
        }
        let counter = new Counter(5);
        for (let i of counter) {
            baseLog(i);                                 // 1,2,3,Exitingearly
            if (i > 2) {
                break;
            }
        }
    }
    {
        let arr = [1, 2, 3, 4, 5];
        let iter = arr[Symbol.iterator]();
        iter.return = function() {
            baseLog('Exitingearly');
            return {done: true};
        }
        for (let i of iter) {
            baseLog(i);                                 // 1,2,3,Exitingearly
            if (i > 2) {
                break;
            }
        }
        for (let i of iter) {
            baseLog(i);                                 // 4,5
        }
    }
}

function testGenerator() {
    {
        // generator function declaration
        function* generatorFn1() {};
        // generator function expression
        let generatorFn2 = function*() {};
        // genrator function as object literal method
        let foo = {
            *generatorFn() {}
        }
        // generator function as class object method
        class Foo {
            *generatorFn() {}
        }
        // generator function as class static method
        class Bar {
            static* generatorFn() {}
        }
    }
    {
        function* generatorFn() {
            return 'foo';
        };
        const g = generatorFn();
        baseLog(g);                 // generatorFn {<suspended>}
        baseLog(g.next);            // ƒ next() { [native code] }
        baseLog(g.next());          // {value: 'foo', done: true}
    }
    {
        function* generatorFn() {
            yield 'foo';
            yield 'bar';
            return 'baz';
        }
        let g = generatorFn();
        baseLog(g.next());          // {value: 'foo', done: false}
        baseLog(g.next());          // {value: 'foo', done: false}
        baseLog(g.next());          // {value: 'bar', done: true}
    }
    {
        function* generatorFn() {
            yield 1;
            yield 2;
            yield 3;
        }
        for (const x of generatorFn()) {
            baseLog(x);             // 1,2,3
        }
    }
    {
        function* nTimes(n) {
            while (n--) {
                yield;
            }
        }
        for (let _ of nTimes(3)) {
            baseLog('foo');         // 'foo','foo','foo'
        }
    }
    {
        function* generatorFn(initial) {
            baseLog(initial);
            baseLog(yield);
            baseLog(yield);
        }
        let g = generatorFn('foo');
        g.next('bar');                  // 'foo'
        g.next('baz');                  // 'baz'
        g.next('qux');                  // 'qux'
    }
    {
        function* generatorFn() {
            return yield 'foo';
        }
        let g = generatorFn();
        baseLog(g.next());          // {value: 'foo', done: false}
        baseLog(g.next('bar'));     // {value: 'bar', done: true}
    }
    {
        function* generatorFn() {
            yield* [1,2,3];
        }
        for (const x of generatorFn()) {
            baseLog(x);             // 1,2,3
        }
    }
    {
        class Foo {
            constructor() {
                this.values = [1,2,3];
            }
            *[Symbol.iterator]() {
                yield* this.values;
            }
        }
        const f = new Foo();
        for (const x of f) {
            baseLog(x);             // 1,2,3
        }
    }
    {
        function* generatorFn() {
            yield* [1,2,3];
        }
        const gf = generatorFn();
        baseLog(gf.next());         // {value: 1, done: false}
        baseLog(gf.return(4));      // {value: 4, done: true}
        baseLog(gf.next());         // {value: undefined, done: true}
        const gf2 = generatorFn();
        for (const x of gf2) {
            if (x > 1) {
                gf2.return(4);
            }
            baseLog(x);             // 1,2
        }
    }
    {
        function* generatorFn() {
            yield* [1,2,3];
        }
        const gf = generatorFn();
        baseLog(gf);               // generatorFn {<suspended>}
        try {
            gf.throw('foo');
        } catch (e) {
            baseLog(e);             // 'foo'
        }
        baseLog(gf);               // generatorFn {<closed>}
        function* generatorFn1() {
            for (const x of [1,2,3]) {
                try {
                    yield x;
                } catch (e) {}
            }
        }
        const gf1 = generatorFn1();
        baseLog(gf1.next());        // {value: 1, done: false}
        gf1.throw('foo');
        baseLog(gf1.next());        // {value: 3, done: false}
    }
}


///////////////////////////////////////////////////////////////////////////////////////
testLog();
testIteratorPattern();
testGenerator();
